<?php
/**
 *redis封装类 
 * codereview 2014-7-5 陈朱尧
 * 相关的小的逻辑问题较多，很多代码没有好好考虑
 * modify xsong 2014-7-7
 * 
 * 
 *
 *用法 ：
  $config = array(
  'host'=>'127.0.0.1',
  'port'=>6379
  );
  $redis = new np_redis_class($config);
  $redis->open();
 */
class np_redis_class {
    private static $redis_connects = array();
    private $redis = null;
    /**
     * @param $config array('host'=>'127.0.0.1', 'port'=>6379)
     */
    public function __construct($config = array())
    {
        if (!is_array($config))
        {
            return;
        }
        $key = $config["host"]."&".$config["port"];
         if (isset(self::$redis_connects[$key]))
         {
            $this->redis = self::$redis_connects[$key];
             $this->_config = $config;
        }else{
            $this->redis = new Redis();
             self::$redis_connects[$key] = $this->redis;
            $this->_config = $config;
        }

    }

    public function __destruct()
    {
        $this->close();
    }

    public function close()
    {
    	if($this->redis ){
    		$this->redis->close();
			//czy 2014-7-5 即然close了，就要=NULL;如果能复用，则不需要清空
			$this->redis=null;
    	}
    }
	
	/*
	 * 返回值问题，一般成功返回TRUE，失败返回FALSE
	*/
    public function open()
    {

	    if(!is_array($this->_config) || empty($this->_config['host']) ||  empty($this->_config['port']) )
	    {
	    	return false;
	    }
	    if($this->redis==null)
	    {
	    	return false;
	    }
        try{
            $re  = $this->redis->ping();
            if ($re ===FALSE)
            {
                $this->__open();
            }
        }catch (Exception $ex)
        {
            $this->__open();
        }

    	return $this->redis;
    }

    private function __open()
    {
        //czy 2014-7-5 要返回检查条件吧
        $result = $this->redis->connect($this->_config['host'],$this->_config['port']);
        if(!$result)
        {
            return false;
        }
        if(isset($this->_config['passwd']) && strlen($this->_config['passwd']) > 0)
        {
            //czy 2014-7-5 要返回检查条件吧
            $result =$this->redis->auth($this->_config['passwd']);
            if(!$result)
            {
                return false;
            }
        }
    }


    public function set($k, $v)
    {
		//czy 2014-7-5 返回值？如果redis不可重用，可能被close，还要检查redis变量是不是空
		if($this->redis==null)
		{
			return false;
		}
    	return $this->redis->set($k, $v);
    }
    public function get($k)
    {
		//czy 2014-7-5 返回值？如果redis不可重用，可能被close，还要检查redis变量是不是空
		if($this->redis==null)
		{
			return false;
		}		
    	return $this->redis->get($k);
    }
    public function delete($k)
    {
		//czy 2014-7-5 返回值？如果redis不可重用，可能被close，还要检查redis变量是不是空
		if($this->redis==null)
		{
			return false;
		}		
    	return $this->redis->delete($k);
    }
    public function exec()
    {
    	//czy 2014-7-5 返回值？如果redis不可重用，可能被close，还要检查redis变量是不是空
    	if($this->redis==null)
    	{
    		return false;
    	}
    	return $this->redis->exec();
    }
    /**
	 *  key的剩余生存时间(以秒为单位)。
	 *  当key不存在或没有设置生存时间时，返回-1 。
     **/
    public function ttl($key)
    {
    	if($this->redis==null)
    	{
    		return false;
    	}
    
    	return $this->redis->ttl($key);
    }
    public function expire($k,$v)
    {
    	if($this->redis==null)
    	{
    		return false;
    	}
    	return $this->redis->expire($k,$v);
    }
    /**
     * 如果键不存在，才设置
     */
    public function setnx($k, $v)
    {
		//czy 2014-7-5 返回值？如果redis不可重用，可能被close，还要检查redis变量是不是空
		if($this->redis==null)
		{
			return false;
		}		
    	return $this->redis->setnx($k, $v);
    }
    public function exists($k)
    {
		//czy 2014-7-5 返回值？如果redis不可重用，可能被close，还要检查redis变量是不是空
    	return $this->redis->exists($k);
    }
    /**
     * 数字递增
     */
    public function incr($k)
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->incr($k);
    }
    /**
     * 数字递减
	 * 要把函数说得很明白，比如$k不存在，
     */    
    public function decr($k)
    {
		//czy 2014-7-5 参数检查
    	return $this->redis->decr($k);
    }    
    /**
     * 取得一个或多个键的值
     */
    public function get_multiple( $keys = array())
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->getMultiple($keys);
    }
    /**
     * 事务建立
     */
    public function multi($key)
    {
    	if($this->redis==null)
    	{
    		return false;
    	}
    	return $this->redis->multi($key);
    }
    /**
     * 将一个或多个值 value 插入到列表 key 的表头
	 * //czy 2014-7-5 返回值 说明写错了，我查文档是列表头？且没有说是字符串类型啊
     */
	public function lpush($k , $v)
	{
		if($this->redis==null)
		{
			return false;
		}		
	    return $this->redis->lpush($k , $v);
	}
	/**
	 * 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
	 */
	public function rpush($k , $v)
	{
		if($this->redis==null)
		{
			return false;
		}		
	    return $this->redis->rpush($k , $v);
	}	
	/**
	 * 返回并移除列表的第一个元素
	 */
    public function lpop($k)
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->lpop($k);
    }
    /**
     * 返回并移除列表的最后一个元素
     */
    public function rpop($k)
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->rpop($k);
    } 
    
    
    /**
     * 返回并移除列表的最后一个元素
     */
    public function lremove($key,$value,$count=0)
    {
        if($this->redis==null)
        {
            return false;
        }
        return $this->redis->lremove($key,$value,$count);
    }
    /**
     * 列表的阻塞式(blocking)弹出原语。
     * 当给定列表内没有任何元素可供弹出的时候，连接将被 BRPOP 命令阻塞，直到等待超时或发现可弹出元素为止。
     * 用法
	 * //czy 2014-7-5 说明呢，龙其是这种阻塞式原语
     * $redis->brPop(array('key1', 'key2'), 10); 
     */
    public function brpop($keys = array(),$time_out=10)
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->brpop($keys,$time_out);
    }
    /**
     * 返回指定键存储在列表中指定的元素。
     *  0第一个元素，1第二个… -1最后一个元素，
     * -2的倒数第二…错误的索引或键不指向列表则返回FALSE
     * @param $k index
     * $redis->lget("test",3)
     */
    public function lget($k,$index) 
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->lget($k, $index);
    }
    public function lsize($k)
    {
    	return $this->redis->lsize($k);
    }
    /**
     * 为列表指定的索引赋新的值,若不存在该索引返回false.
     */
    public function lset($k , $index,$v)
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->lset($k , $index, $v);
    }    
    /**
     * 返回在该区域中的指定键列表中开始到结束存储的指定元素，
	 * //czy 2014-7-5 返回值 如果元数不足，错位等，如何返回
     * lGetRange(key, start, end)。0第一个元素，1第二个元素… -1最后一个元素，-2的倒数第二…
     */
    public function  lgetrange($k , $begin_index, $end_index)
    {
		if($this->redis==null)
		{
			return false;
		}    	
    	return $this->redis->lgetrange($k , $begin_index, $end_index);
    }
    /**
     * redis有非常多的操作方法，只封装了一部分
     * 拿着这个对象就可以直接调用redis自身方法 
     */
    public function get_redis()
    {
    	return $this->redis;
    }

    /**
     * @param $hash_key
     * @param $key
     * @param string $value
     * @return bool|int
     * @author huayi.cai
     *增加 key 指定的哈希集中指定字段的数值。
     * 如果 key 不存在，会创建一个新的哈希集并与 key 关联。
     * 如果字段不存在，则字段的值在该操作执行前被设置为 0
     */
    public function hIncrBy($hash_key, $key, $value = "")
    {
        //hash名称和里面的key不能为空
        if(empty($hash_key) || empty($key))
        {
            return false;
        }
        return $this->redis->hIncrBy($hash_key, $key, $value);
    }

    /**
     * @param $hash_key
     * @param $key
     * @param $value
     * @return bool|int
     * 设置 key 指定的哈希集中指定字段的值。
     * 该命令将重写所有在哈希集中存在的字段。
     * 如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联
     * @author huayi.cai
     */
    public function hSet($hash_key, $key, $value)
    {
        //hash名称和里面的key不能为空
        if(empty($hash_key) || empty($key))
        {
            return false;
        }
        return $this->redis->hSet($hash_key, $key, $value);
    }

    /**
     * @param $hash_key
     * @param $key
     * @return bool
     * 返回字段是否是 key 指定的哈希集中存在的字段。
     * @author huayi.cai
     */
    public function hExists($hash_key, $key)
    {
        //hash名称和里面的key不能为空
        if(empty($hash_key) || empty($key))
        {
            return false;
        }
        return $this->redis->hExists($hash_key, $key);
    }

    /**
     * @param $hash_key
     * @return array|bool
     * @author huayi.cai
     * 返回 key 指定的哈希集中所有字段的名字。
     */
    public function hKeys($hash_key)
    {
        if(empty($hash_key))
        {
            return false;
        }
        return $this->redis->hKeys($hash_key);
    }

    /**
     * @param $hash_key
     * @param $key
     * @return bool|string
     * @author huayi.cai
     * 返回字段是否是 key 指定的哈希集中存在的字段。
     */
    public function hGet($hash_key, $key)
    {
        //hash名称和里面的key不能为空
        if(empty($hash_key) || empty($key))
        {
            return false;
        }
        return $this->redis->hGet($hash_key, $key);
    }

    /**
     * @param $hash_key
     * @param $key
     * @return bool|int
     * @author huayi.cai
     * 从 key 指定的哈希集中移除指定的域。
     * 在哈希集中不存在的域将被忽略。
     * 如果 key 指定的哈希集不存在，它将被认为是一个空的哈希集，该命令将返回0。
     */
    public function hDel($hash_key, $key)
    {
        //hash名称和里面的key不能为空
        if(empty($hash_key) || empty($key))
        {
            return false;
        }
        return $this->redis->hDel($hash_key, $key);
    }

    /**
     * @param $list_key
     * @author huayi.cai
     * 返回存储在 key 里的list的长度。
     * 如果 key 不存在，那么就被看作是空list，并且返回长度为 0。
     * 当存储在 key 里的值不是一个list的话，会返回error。
     */
    public function lLen($list_key = "")
    {
        return $this->redis->lLen($list_key);
    }

    /**
     * @param $list_key
     * @param $start
     * @param $end
     * @return array|bool
     * @author huayi.cai
     * 返回存储在 key 的列表里指定范围内的元素。
     * start 和 end 偏移量都是基于0的下标，即list的第一个元素下标是0（list的表头），第二个元素下标是1，以此类推。
     * 偏移量也可以是负数，表示偏移量是从list尾部开始计数。 例如， -1 表示列表的最后一个元素，-2 是倒数第二个，以此类推。
     */
    public function lrange($list_key, $start, $end)
    {
        if(empty($list_key) || !is_numeric($start)  || !is_numeric($end))
        {
            return false;
        }
        return $this->redis->lrange($list_key, $start, $end);
    }
    /*
  * redis 模拟MYSQL 行为的方法集合
  * */
//    默认模式进行操作
    const REDIS_DEFAULT_MODE="default";
//    pipeline模式进行批量操作
//    const REDIS_PIPELINE_MODE="pipeline";
//    执行正常
    const REDIS_RUN_OK="000000";
//    参数错误码
    const REDIS_PARAMS_ERROR="000001";
// REDIS报错
    const REDIS_RUN_ERROR="000002";
// TEMP表的缓存时间为30秒
    const REDIS_TEMP_TABLE_EXP_TIME=30;
    // PHP数组内存上限，超过上限采用循环读取的方式
    const REDIS_TEMP_PHP_ARRAY_LIMIT=100;
//索引KEY前缀定义
    const REDIS_HASH_TABLE_KEY_INDEX_PIX="hash.table.index:";
//索引compareKEY前缀定义
    const REDIS_HASH_TABLE_KEY_COMPARE_PIX="hash.table.compare:";
//TEMP表的前缀申明
    const REDIS_HASH_TEMP_TABLE_PIX="hash.table.temp:";
//    CACHE表的前缀申明
    const REDIS_HASH_CACHE_PIX="hash.table.cache:";
//数据HASH表名前缀定义
    const REDIS_HASH_TABLE_PIX="hash.table.data:";
//数据表结构存储KEY前缀声明
    const REDIS_HASH_TABLE_STRUCTURE="hash.table.structure:";
//唯一ID的标示方式
    const REDIS_HASH_TABLE_MAIN_KEY="#id";
//现有表存储
    const REDIS_HASH_TABLE_STORE = "hash.table.store";
// 当前选择的表名
    private $hash_table_current_table="";
// 当前做筛选后查询要用到的TEMP表
    private $hash_table_temp_table="";
//当前模糊查询的扩展TEMP表名
    private $hash_table_like_temp_table="";
//    临时总数存储
    private $hash_temp_len=0;
// 排序配置
    private $hash_table_sort_option=array();
// 错误记录
    private $hash_table_redis_errors=array();
// 临时保存当前表的字段
    private $hash_table_fields=array();
// 搜索条件临时存储地址
    private $hash_table_query_questions=array();
//存储临时的index索引
    private $hash_table_indexs = array();
//存储临时的compare索引
    private $hash_table_compares = array();
// 如果为临时表，存储表的过期时间
    private $hash_table_exp = 0;
//  模糊查询条件
    private $hash_table_like_question = array();
    //  查询比较条件
    private $hash_table_compare_question = array();
//    多个条件产生的临时表组
    private $hash_table_where_temp_table  =array();
//    多个条件产生的排除临时表组
    private $hash_table_except_where_temp_table = array();


//    多个where条件之间连接方式
    public $hash_table_where_link_type = "and";
//多个except_where条件间的连接方式
    public $hash_table_except_where_link_type = "and";
//    获取实际的表名
    private function hash_table_get_table_name($table_name)
    {
        $hash_table = self::REDIS_HASH_TABLE_PIX.$table_name;
        return $hash_table;
    }
//    根据传入的数据主键，获取数据表的实际主键
    private function hash_table_get_table_row_id($table_name,$id)
    {
        $hash_table = self::REDIS_HASH_TABLE_PIX.$table_name;
        $hash_table_row_id=$hash_table.":".$id;
        return $hash_table_row_id;
    }


//    获取索引列表的主键
    private function hash_table_get_list_index($table_name,$index_name,$index_value="")
    {
        $hash_table = self::REDIS_HASH_TABLE_KEY_INDEX_PIX.$table_name;
        $hash_table_index_id=$hash_table.".".$index_name.":".$index_value;
        return $hash_table_index_id;
    }

    //    获取比较查询索引列表的主键
    private function hash_table_get_list_compare($table_name,$comapre_name)
    {
        $hash_table = self::REDIS_HASH_TABLE_KEY_COMPARE_PIX.$table_name;
        $hash_table_index_id=$hash_table.".".$comapre_name;
        return $hash_table_index_id;
    }

    /**
     * 创建表，只需要创建一次，如果表已存在，则创建失败
     * @param $table_name 表名
     * @param $fields 表的字段
     * array(key1,key2,key3,key4)
     * @param $indexs 需要创建的索引字段
     *  array(key1,key2,key3)
     * @param $exp_time 临时表失效时间，不传为永久表
     * @return bool
     * FALSE 为错误，用hash_table_errors()获取最后一条错误信息
     * TRUE为成功
     */
    public function hash_create_table($table_name,$fields,$indexs,$compares=array(),$exp_time=0){
        if (empty($table_name))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[create]table name is empty"
            );
            return FALSE;
        }
        if (empty($fields) || !is_array($fields))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[create]fields is empty"
            );
            return FALSE;
        }

        $index_struc = self::REDIS_HASH_TABLE_STRUCTURE.$table_name;

//        如果表已存在，则无法再次创建
        if ($this->exists($index_struc))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_RUN_ERROR,
                "reason"=>"[create]table {$table_name} is exists"
            );
            return FALSE;
        }

        $structure = array();
        $structure["fields"]=$fields;
        $structure["indexs"]=$indexs;
        $structure["compares"]=$compares;
        $structure["exp"]=$exp_time;

//        将表结构存储起来
        $structure = serialize($structure);
        $this->set($index_struc,$structure);
//        如果是临时表，则创建过期时间
        if ($exp_time > 0){
            $this->get_redis()->expire($index_struc,$exp_time);
        }

        $this->get_redis()->sAdd(self::REDIS_HASH_TABLE_STORE,$table_name);
        return TRUE;
    }

    /**
     * 为表创建新字段
     * 终结函数
     * @param $field_name  字段名
     * @param string $default_value 字段默认值
     * @param bool $index_flag  是否为查询索引
     * @param bool $compare_flag  是否为比较索引
     * @return bool TRUE 成功 FALSE 失败
     */
    public function hash_create_field($field_name,$default_value = "" , $index_flag = false,$compare_flag = false)
    {
        //      检查是否已经存在表
        if (empty($this->hash_table_current_table))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[create_field]table name is empty"
            );
            return FALSE;
        }

        if (empty($field_name))
        {
            return FALSE;
        }
        $index_struc = self::REDIS_HASH_TABLE_STRUCTURE.$this->hash_table_current_table;
        $struc = array();
        if (in_array($field_name,$this->hash_table_fields))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[create_field]field is exists"
            );
            return FALSE;
        }

        if (!in_array($field_name,$this->hash_table_fields))
        {
            $this->hash_table_fields[] = $field_name;
        }

        $struc["fields"] = $this->hash_table_fields;
        if ($index_flag)
        {
            if (!in_array($field_name,$this->hash_table_indexs))
            {
                $this->hash_table_indexs[] = $field_name;
            }
            $struc["indexs"] = $this->hash_table_indexs;
        }
        if ($compare_flag)
        {
            if (!in_array($field_name,$this->hash_table_compares))
            {
                $this->hash_table_compares[] = $field_name;
            }
            $struc["compares"] = $this->hash_table_compares;
        }
        $struc = serialize($struc);
        $this->get_redis()->set($index_struc,$struc);
        $params = array();
        $params[$field_name] = $default_value;
        $this->hash_modify($params);
    }

    /**
     * @param $params 需要修改的键值对
     * @return bool
     */
    public function hash_modify($params)
    {
        //      检查是否已经存在表
        if (empty($this->hash_table_current_table))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[create_field]table name is empty"
            );
            return FALSE;
        }
        $this->hash_where_order_query(false);
        $indexs = array();
        $compares = array();
        $p = array();
        foreach ($params as $key=>$value)
        {
            if (!in_array($key,$this->hash_table_fields))
            {
                continue;
            }
            $p[$key] = $value;
            if (in_array($key,$this->hash_table_indexs))
            {
                $indexs[] = $key;
            }
            if (in_array($key,$this->hash_table_compares))
            {
                $compares[] = $key;
            }
        }

        if (empty($p))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[create_field]field_name is exists"
            );
            return false;
        }

        unset($params);
        $metadata = array();
        $metadata["indexs"] = $indexs;
        $metadata["compares"] = $compares;
        $metadata["params"] = $p;
        $this->_hash_exec_each_data("_hash_modify_metadata",$metadata);
        return TRUE;
    }


    private function _hash_modify_metadata($re,$metadata)
    {
        $indexs = $metadata["indexs"];
        $compares = $metadata["compares"];
        $params = $metadata["params"];
        foreach ($re as $id)
        {
            $vid = $this->hash_table_get_table_row_id($this->hash_table_current_table,$id);

            if (!$this->get_redis()->exists($vid))
            {
                continue;
            }

            $index_values = $this->get_redis()->hMGet($vid,$indexs);


            $this->get_redis()->multi(Redis::PIPELINE);
            $this->get_redis()->hMset($vid,$params);
            foreach ($indexs as $index)
            {
                $index_id = $this->hash_table_get_list_index($this->hash_table_current_table,$index,$index_values[$index]);
                $this->get_redis()->sRem($index_id,$id);
                $index_id = $this->hash_table_get_list_index($this->hash_table_current_table,$index,$params[$index]);
                $this->get_redis()->sAdd($index_id,$id);
            }
            foreach ($compares as $compare)
            {
                $compare_key = $this->hash_table_get_list_compare($this->hash_table_current_table,$compare);
                $this->get_redis()->zAdd($compare_key,(float)$params[$compare],$id);
            }
            $this->get_redis()->exec();
        }
    }
    /**
     * 检查表是否存在
     * @param $table_name 表名
     * @return bool
     * TRUE 表存在
     * FALSE 表不存在
     */
    public function hash_table_exists($table_name)
    {
        $index_struc = self::REDIS_HASH_TABLE_STRUCTURE.$table_name;

        if ($this->exists($index_struc))
        {
            return TRUE;
        }else{
            return FALSE;
        }
    }

    /**
     * 更新或插入数据,当ID不存在时，做插入操作，当ID存在时，做更新操作。
     * @param $id  数据ID主键
     * @param $params  数据内容
     * array(key=>value,key=>value)
     * @return FALSE 失败 用hash_table_errors()获取最后一条错误信息
     * 其他为成功
     * @author 帅富元
     * @date 2015/01/21
     */
    public function hash_update($id,$params)
    {
//      检查是否已经存在表
        if (empty($this->hash_table_current_table))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[update]table name is empty"
            );
            return FALSE;
        }
//      检查主键是否存在
        if (empty($id))
        {
            $this->hash_table_redis_errors[]= array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=> "[update]id is empty"
            );
            return FALSE;
        }
//        检查参数是否存在
        if (!is_array($params))
        {
            $this->hash_table_redis_errors[]= array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[update] params is empty"
            );
            return FALSE;
        }


        $indexs = $this->hash_table_indexs;
        $fields = $this->hash_table_fields;
        $compares = $this->hash_table_compares;
        $exp = $this->hash_table_exp;

//        检查是否存在非法不存在的字段。
//        $diff_keys = array_diff_key($params,$fields);
        foreach ($params  as $field=>$value)
        {
            if  (!in_array($field,$fields)){
                $this->hash_table_redis_errors[]= array(
                    "code"=>self::REDIS_PARAMS_ERROR,
                    "reason"=>"[update] {$field} field is not exists"
                );
                return FALSE;
            }
        }

//        检查是插入还是更新
        $hash_table_row_id=$this->hash_table_get_table_row_id($this->hash_table_current_table,$id);
        $insert_flag = FALSE;
        if (!$this->exists($hash_table_row_id))
        {
            $insert_flag = TRUE;
        }
//      开启事务执行
//        $this->get_redis()->multi();

//        创建主键的索引
        if ($insert_flag == TRUE)
        {
            $index_id_key=$this->hash_table_get_list_index($this->hash_table_current_table,self::REDIS_HASH_TABLE_MAIN_KEY);
            $this->get_redis()->sAdd($index_id_key,$id);
        }else{
            $exists_values = $this->get_redis()->hGetAll($hash_table_row_id);
        }
        $this->get_redis()->multi(Redis::PIPELINE);
//        如果是插入操作，并且需要创建索引，则创建索引
        if ( is_array($indexs)){
            foreach ($indexs as $index_name){
                if (array_key_exists($index_name,$params)){
                    if ($insert_flag == FALSE)
                    {
//                        $value = $this->get_redis()->hGet($hash_table_row_id,$index_name);
                        $value = $exists_values[$index_name];
                        $index_field_key=$this->hash_table_get_list_index($this->hash_table_current_table,$index_name,$value);
                        $this->get_redis()->sRem($index_field_key,$id);
                    }
//                    当且仅当值不为空时，才创建索引
                    if (strlen($params[$index_name]) > 0)
                    {
                        $index_name_key=$this->hash_table_get_list_index($this->hash_table_current_table,$index_name,$params[$index_name]);
                        $this->get_redis()->sAdd($index_name_key,$id);
                        if ($exp > 0)
                        {
                            $this->get_redis()->expire($index_name_key,$exp);
                        }
                    }
                }
            }
        }

        if (is_array($compares))
        {

            foreach ($compares as $compare_name){
                if (array_key_exists($compare_name,$params)){

                    $compare_field_key=$this->hash_table_get_list_compare($this->hash_table_current_table,$compare_name);

                    $compare_value = (float)$params[$compare_name];
                   $this->get_redis()->zAdd($compare_field_key,$compare_value,$id);
                    if ($exp > 0)
                    {
                        $this->get_redis()->expire($compare_field_key,$exp);
                    }
                }
            }
        }

        $this->get_redis()->hMset($hash_table_row_id,$params);
        if ($exp > 0)
        {
            $this->get_redis()->expire($hash_table_row_id,$exp);
            $this->get_redis()->expire($index_id_key,$exp);
        }
//      执行所有事务
        $result = $this->get_redis()->exec();
        $result = end($result);
//        $result = $result[$count_index-1];
//        var_dump($result);var_dump($this->get_redis()->zRange($compare_field_key,0,100000));die;

        if ($result===FALSE){
            $this->hash_table_redis_errors[]= array(
                "code"=>self::REDIS_RUN_ERROR,
                "reason"=>"[update]redis->hMset is fail"
            );
            return FALSE;
        }else{
            return TRUE;
        }

    }

    /**
     * 获取表对象
     * @param $table_name 要选择的表名
     * @return $this 表对象
     */
    public function hash_table($table_name)
    {
        if (empty($table_name)){
            $this->hash_table_redis_errors[] = array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[table]table name is empty"
            );
            return $this;
        }

//      检查表是否创建
        if (!$this->exists(self::REDIS_HASH_TABLE_STRUCTURE.$table_name))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_RUN_ERROR,
                "reason"=>"[table]table  is not exists"
            );
            return $this;
        }
//      检查表结构是否存在
        $structure = $this->get(self::REDIS_HASH_TABLE_STRUCTURE.$table_name);
        if (empty($structure))
        {
            $this->hash_table_redis_errors[]=array(
                "code"=>self::REDIS_RUN_ERROR,
                "reason"=>"[table]table structure  is not exists"
            );
            return $this;
        }

//        清空初始化数据
        $structure = unserialize($structure);
        $this->hash_temp_len = 0;
        $this->hash_table_fields = $structure["fields"];
        $this->hash_table_indexs = empty($structure["indexs"])?array():$structure["indexs"];
        $this->hash_table_exp = empty($structure["exp"])?0:$structure["exp"];
        $this->hash_table_compares = empty($structure["compares"])?array():$structure["compares"];
        $this->hash_table_temp_table = $this->hash_table_get_list_index($table_name,self::REDIS_HASH_TABLE_MAIN_KEY);
        $this->hash_table_current_table = $table_name;
        $this->hash_table_where_temp_table = array();
        $this->hash_table_sort_option = array();
        $this->hash_table_redis_errors = array();
        $this->hash_table_like_question = array();
        $this->hash_table_compare_question = array();
        $this->hash_table_query_questions = array();
        $this->hash_table_except_where_temp_table = array();

        $this->hash_table_where_link_type = "and";
        $this->hash_table_except_where_link_type = "and";


        return $this;
    }

    /**
     * 效率问题，暂不支持该方法
     * @param $question
     * @return $this
     */
//    public function hash_like($question,$action="and",$except=false)
//    {
//        if (empty($this->hash_table_current_table))
//        {
//            return $this;
//        }
//
//        if (!is_array($question))
//        {
//            return $this;
//        }
////        暂不支持，如果用keys来模糊匹配的话，会影响效率，官方文档已明确定义
////        最好不要使用keys方法
//
//        $this->hash_table_like_question=array($question,$action,$except);
//
//        return $this;
//    }

    /**
     * 表查询bi条件添加
     * @param $question 条件数组
     * array(key1=>array(1,2,3),key2=>10)
     * 如果action为and 则代表  (key1 in (1,2,3)) and key2=10
     * 如果action为or 则代表  (key1 in (1,2,3)) or key2=10
     * @param string $action  一级数组用何种方式连接  and | or
     * @return $this 表对象
     */
    public function hash_compare($question,$action="and")
    {
        if (empty($this->hash_table_current_table))
        {
            return $this;
        }


        if (!is_array($question))
        {
            $this->hash_table_redis_errors[] = array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[compare] question  is empty"
            );
            return $this;
        }
//      将条件存储起来
        $this->hash_table_compare_question[] = array($question,$action,FALSE);

//        $this->_create_temp_table($question,$action);

        return $this;
    }

    /**
     * 排除表查询对比条件添加
     * @param $question 条件数组
     * array(key1=>array(1,2,3),key2=>10)
     * 如果action为and 则代表  (key1 in (1,2,3)) and key2=10
     * 如果action为or 则代表  (key1 in (1,2,3)) or key2=10
     * @param string $action  一级数组用何种方式连接  and | or
     * @return $this 表对象
     */
    public function hash_except_compare($question,$action="and")
    {
        if (empty($this->hash_table_current_table))
        {
            return $this;
        }


        if (!is_array($question))
        {
            $this->hash_table_redis_errors[] = array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[compare] question  is empty"
            );
            return $this;
        }
//      将条件存储起来
        $this->hash_table_compare_question[] = array($question,$action,TRUE);

//        $this->_create_temp_table($question,$action);

        return $this;
    }


    /**
     * 表查询条件添加
     * @param $question 条件数组
     * array(key1=>array(1,2,3),key2=>10)
     * 如果action为and 则代表  (key1 in (1,2,3)) and key2=10
     * 如果action为or 则代表  (key1 in (1,2,3)) or key2=10
     * @param string $action  一级数组用何种方式连接  and | or
     * @return $this 表对象
     */
    public function hash_where($question,$action="and")
    {
        if (empty($this->hash_table_current_table))
        {
            return $this;
        }


        if (!is_array($question))
        {
            $this->hash_table_redis_errors[] = array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[where] question  is empty"
            );
            return $this;
        }
//      将条件存储起来
        $this->hash_table_query_questions[] = array($question,$action,FALSE);

//        $this->_create_temp_table($question,$action);

        return $this;
    }

    /**
     * 排除表查询条件添加
     * @param $question 条件数组
     * array(key1=>array(1,2,3),key2=>10)
     * 如果action为and 则代表  (key1 in (1,2,3)) and key2=10
     * 如果action为or 则代表  (key1 in (1,2,3)) or key2=10
     * @param string $action  一级数组用何种方式连接  and | or
     * @return $this 表对象
     */
    public function hash_except_where($question,$action="and")
    {
        if (empty($this->hash_table_current_table))
        {
            return $this;
        }


        if (!is_array($question))
        {
            $this->hash_table_redis_errors[] = array(
                "code"=>self::REDIS_PARAMS_ERROR,
                "reason"=>"[where] question  is empty"
            );
            return $this;
        }
//      将条件存储起来
        $this->hash_table_query_questions[] = array($question,$action,TRUE);

//        $this->_create_temp_table($question,$action);

        return $this;
    }

//    临时表创建
    private function _create_temp_table($question,$action,$except,$cache)
    {
        $reset = array();
        $temp_query_key = self::REDIS_HASH_TEMP_TABLE_PIX."QUERY:";
        foreach ($question as $k=>$v)
        {
//                如果是查询主键
            if ($k===self::REDIS_HASH_TABLE_MAIN_KEY)
            {
                $_main_key_set=$temp_query_key.MD5(serialize($v));
                if (!$this->exists($_main_key_set))
                {
                    if (is_array($v)){
                        $tmp_sdd_values = array();
                        $tmp_sdd_values = $v;
                        array_unshift($tmp_sdd_values,$_main_key_set);

                        call_user_func_array(array($this->get_redis(),"sAdd"),$tmp_sdd_values);
                        unset($tmp_sdd_values);
                        $id_key = $this->hash_table_get_list_index($this->hash_table_current_table,self::REDIS_HASH_TABLE_MAIN_KEY);
                        $this->get_redis()->sInterStore($_main_key_set,$id_key,$_main_key_set);


                    }else{
                        if ($this->exists($this->hash_table_get_table_row_id($this->hash_table_current_table,$v)))
                        {
                            $this->get_redis()->sAdd($_main_key_set, $v);
                        }
                    }
                    $this->get_redis()->expire($_main_key_set,self::REDIS_TEMP_TABLE_EXP_TIME);
                }
                $reset[] = $_main_key_set;
                continue;
            }

            if (is_array($v) && isset($v[0]))
            {
                $_or_reset=array();

                foreach ($v as $v1)
                {
                    $index_key_name_1=$this->hash_table_get_list_index($this->hash_table_current_table,$k,$v1);
                    $_or_reset[] = $index_key_name_1;
                }
                if (isset($_or_reset[1])){
                    $_or_k=$temp_query_key.MD5(serialize($_or_reset));
                    if (!$this->exists($_or_k) || $cache===false)
                    {
                        array_unshift($_or_reset,$_or_k);
                        call_user_func_array(array($this->get_redis(),"sUnionStore"),$_or_reset);
                        $this->get_redis()->expire($_or_k,self::REDIS_TEMP_TABLE_EXP_TIME);
                    }
                }else{
                    $_or_k = $_or_reset[0];
                }
                $reset[] = $_or_k;
            }else{
                $index_key_name=$this->hash_table_get_list_index($this->hash_table_current_table,$k,$v);
                if ($this->exists($index_key_name))
                {
                    $reset[] = $index_key_name;
                }else{
                    $reset[] = np_guid_rand();
                }
            }
        }


        $temp_table_name = $this->_create_where_database($reset,$action,$cache);
        if (!empty($temp_table_name))
        {
            if ($except === TRUE)
            {
                $this->hash_table_except_where_temp_table[] = $temp_table_name;
            }else{
                $this->hash_table_where_temp_table[] = $temp_table_name;
            }
        }

//
//        if (count($reset)> 1){
//            $temp_table_key=$temp_query_key.MD5(serialize($reset)."##".$action);
////            $this->hash_table_temp_table=$temp_table_key;
//            if (!$this->exists($temp_table_key) || $cache===false)
//            {
//                array_unshift($reset,$temp_table_key);
//                if ($action=="and")
//                {
//                    $method = "sInterStore";
//                }else{
//                    $method = "sUnionStore";
//                }
//                $this->temp_len=call_user_func_array(array($this->get_redis(),$method),$reset);
//                $this->get_redis()->expire($temp_table_key,self::REDIS_TEMP_TABLE_EXP_TIME);
//            }
//            $this->hash_table_where_temp_table[] = $temp_table_key;
//            return $temp_table_key;
//        }else if (!empty($reset))
//        {
//            $this->hash_table_where_temp_table[] = $reset[0];
//            return $reset[0];
//        }
    }

    private function _create_where_database($reset,$action,$cache)
    {
        $temp_query_key = self::REDIS_HASH_TEMP_TABLE_PIX."QUERY:";
        if (count($reset)> 1){
            $temp_table_key=$temp_query_key.MD5(serialize($reset)."##".$action);
//            $this->hash_table_temp_table=$temp_table_key;
            if (!$this->exists($temp_table_key) || $cache===false)
            {
                array_unshift($reset,$temp_table_key);
                if ($action=="and")
                {
                    $method = "sInterStore";
                } else if ($action=="except")
                {
                    $method = "sDiffStore";
                }else{
                    $method = "sUnionStore";
                }
                $this->temp_len=call_user_func_array(array($this->get_redis(),$method),$reset);
                $this->get_redis()->expire($temp_table_key,self::REDIS_TEMP_TABLE_EXP_TIME);
            }
//            $this->hash_table_where_temp_table[] = $temp_table_key;
            return $temp_table_key;
        }else if (!empty($reset))
        {
//            $this->hash_table_where_temp_table[] = $reset[0];
            return $reset[0];
        }else{
            return NULL;
        }
    }


//

    /**
     * 为查询结果排序
     * @param string $orderkey  需要排序的字段
     * @param string $ordertype 排序的类型 asc|desc
     * @return $this
     */
    public function hash_orderby($orderkey="",$ordertype="desc")
    {
        if (empty($orderkey)){
//            如果排序集合为空，那么指定一个不存在的排序集合，用于跳过排序。
            $orderkey = np_guid_rand();
        }else{
            $table_index_pix = $this->hash_table_get_table_name($this->hash_table_current_table);
            $orderkey = $table_index_pix.":*->".$orderkey;
        }
        $this->hash_table_sort_option["by"]=$orderkey;
        $this->hash_table_sort_option["sort"]=$ordertype;

        return $this;
    }

    private function _create_compare_database($question,$action,$except,$cache)
    {
        $reset = array();

        foreach ($question as $key=>$value){

            $compare_key = $this->hash_table_get_list_compare($this->hash_table_current_table,$key);
            if (is_array($value))
            {
                $start = is_numeric($value[0])?$value[0]:"-inf";
                $end =  is_numeric($value[1])?$value[1]:"+inf";
                $temp_key = $this->_loop_get_compare_keys_result($compare_key,$start,$end,$cache);
                $reset[] = $temp_key;
            }
         }

        $compare_key =  $this->_create_where_database($reset,$action,$cache);
        if ($compare_key!==NULL){
//            $this->hash_table_where_temp_table[] = $compare_key;
            if ($except === TRUE)
            {
                $this->hash_table_except_where_temp_table[] = $compare_key;

            }else{
                $this->hash_table_where_temp_table[] = $compare_key;
            }
        }
    }


    private function _loop_get_compare_keys_result($compare_key,$start,$end,$cache)
    {

        $count = $this->get_redis()->zCount($compare_key,$start,$end);
        $temp_query_key = self::REDIS_HASH_TEMP_TABLE_PIX."COMPARE:".MD5($compare_key."##".$start."##".$end);
        if ($this->exists($temp_query_key) && $cache===TRUE)
        {
            return $temp_query_key;
        }

        if ($cache===FALSE)
        {
            $this->get_redis()->delete($temp_query_key);
        }

        if ($count <= self::REDIS_TEMP_PHP_ARRAY_LIMIT)
        {
            $re = $this->get_redis()->zRangeByScore($compare_key,$start,$end);
            if (is_array($re) && count($re) > 0)
            {
                array_unshift($re,$temp_query_key);
                call_user_func_array(array($this->get_redis(),"sAdd"),$re);
            }
        }else{
            $t_limit = 0;
            while($t_limit <= $count-1){
                $option = array();
                $option["limit"]=array($t_limit,self::REDIS_TEMP_PHP_ARRAY_LIMIT);
                $re = $this->get_redis()->zRangeByScore($compare_key,$start,$end,$option);
                if (is_array($re) && count($re) > 0)
                {
                    $t_limit = $t_limit + self::REDIS_TEMP_PHP_ARRAY_LIMIT;
                    array_unshift($re, $temp_query_key);
                    call_user_func_array(array($this->get_redis(), "sAdd"), $re);
                }
            }
        }

        $this->get_redis()->expire($temp_query_key,self::REDIS_TEMP_TABLE_EXP_TIME);
        return $temp_query_key;
    }


    private function hash_where_order_query($cache=true)
    {

        foreach ($this->hash_table_query_questions as $question)
        {
            if (!empty($question))
            {
                    $this->_create_temp_table($question[0],$question[1],$question[2],$cache);
            }
        }
//        var_dump($this->hash_table_compare_question);die;
        foreach ($this->hash_table_compare_question as $question)
        {

            if (!empty($question))
            {
                $this->_create_compare_database($question[0],$question[1],$question[2],$cache);
            }
        }

        $where_database_name = $this->_create_where_database($this->hash_table_where_temp_table,$this->hash_table_where_link_type,$cache);

        $except_where_database_name = $this->_create_where_database($this->hash_table_except_where_temp_table,$this->hash_table_except_where_link_type,$cache);

        $where_query_array = array();
        if ($where_database_name!==NULL)
        {
            $where_query_array[] = $where_database_name;
            $this->hash_table_temp_table = $where_database_name;
        }else{
            $where_query_array[] = $this->hash_table_temp_table;
        }

        if ($except_where_database_name !== NULL)
        {
            $where_query_array[] = $except_where_database_name;
        }

        $where_query_key_name = $this->_create_where_database($where_query_array,"except",$cache);

        if ($where_query_key_name!==NULL)
        {

            $this->hash_table_temp_table = $where_query_key_name;
        }

//        if (!empty($this->hash_table_like_question))
//        {
//            $this->_create_like_temp_table($this->hash_table_like_question[0],$this->hash_table_like_question[1],$cache);
//        }

//        if (!empty($this->hash_table_like_temp_table) )
//        {
//            $this->get_redis()->sInterStore($this->hash_table_temp_table,$this->hash_table_temp_table,$this->hash_table_like_temp_table);
//        }

        $this->hash_temp_len = $this->get_redis()->sCard($this->hash_table_temp_table);
//        $this->get_redis()->exec();
//        var_dump($this->hash_table_temp_table);
//        var_dump($this->get_redis()->sMembers($this->hash_table_temp_table));
//        $re = $this->get_redis()->sort($this->hash_table_temp_table ,$this->hash_table_sort_option);
//
//        return $re;
    }
    /**
     * 执行查询结果
     * @param $fields 需要显示的字段
     * @param $limit  偏移分页数量
     * array(0,10)
     * @param int $cache_time  是否对查询结果做缓存，如果做缓存则传递>0的时间，代表过期时间（单位秒）
     * @param bool $with_count 查询结果是否返回count数
     * @return array 返回的数据
     * FALSE 返回失败  用hash_table_errors()获取最后一条错误信息
     */
    public function hash_query($fields,$limit,$cache_time=0,$with_count=FALSE)
    {
        if (empty($this->hash_table_current_table)){
            return FALSE;
        }
        $table_index_pix = $this->hash_table_get_table_name($this->hash_table_current_table);
        if ($fields==="*")
        {
            $fields=$this->hash_table_fields;
        }else{
            if (strlen($fields)>0){
                $fields = explode(",",$fields);
            }else{
                $fields =array();
            }
        }
        $_fields = array();
        foreach ($fields as $key=>$field)
        {
            $_fields[$key] = $table_index_pix.":*->".$field;
        }

        array_unshift($_fields,"#");

        if (!is_array($limit)){
            $limit=array(0,1000);
        }

        if (empty($this->hash_table_sort_option["by"]))
        {
            $this->hash_table_sort_option["by"] = np_guid_rand();
        }

        if (empty($this->hash_table_sort_option["sort"]))
        {
            $this->hash_table_sort_option["sort"] = "asc";
        }

        $this->hash_table_sort_option["limit"]=$limit;
        $this->hash_table_sort_option["get"]=$_fields;

//        创建搜索结果的缓存KEY
        $params_cache_key = array();
        $params_cache_key["questions"]=$this->hash_table_query_questions;
        $params_cache_key["compare"]=$this->hash_table_compare_question;
        $params_cache_key["table"]=$this->hash_table_current_table;
        $params_cache_key["sort"]=$this->hash_table_sort_option;
        $params_cache_key["count"]=$with_count;

        $params_cache_key = self::REDIS_HASH_CACHE_PIX . MD5(serialize($params_cache_key));
//        如果缓存命中，则直接返回缓存
        if ($this->exists($params_cache_key))
        {
            $cache_data = $this->get($params_cache_key);
            $cache_data = unserialize($cache_data);
            return $cache_data;
        }


        $this->hash_where_order_query();
        $re = $this->get_redis()->sort($this->hash_table_temp_table ,$this->hash_table_sort_option);
        $field_count=count($fields);
        $result=array();
        if (is_array($re))
        {
            foreach ($re as $k=>$v)
            {
                $k1 = $k % ($field_count+1);
                $n = floor($k / ($field_count+1));
                if ($k1 === 0){
                    $result[$n][self::REDIS_HASH_TABLE_MAIN_KEY]= $v;
                    continue;
                }
                $result[$n][$fields[$k1-1]]= $v;
            }
        }


        unset($re);

        if ($with_count===TRUE){
            $data = array("count"=>$this->hash_temp_len,"data"=>$result);
        }else
        {
            $data = $result;
        }

        if ($cache_time > 0){
            $this->set($params_cache_key,serialize($data));
            $this->get_redis()->expire($params_cache_key,$cache_time);
        }
        return $data;
    }

    /**
     * 执行统计结果
     * @return int 返回统计的数值
     * FALSE  统计失败，用hash_table_errors()获取最后一条错误信息
     */
    public function hash_count($cache=true){
        if (empty($this->hash_table_current_table)){
            return FALSE;
        }
//        $this->get_redis()->multi();

        $this->hash_where_order_query($cache);
        return $this->hash_temp_len;
    }

    /**
     * 执行删除操作
     * @param array $limit 起止条数
     * @return bool|int
     * int 返回统计的数值 TRUE 成功
     * FALSE  统计失败，用hash_table_errors()获取最后一条错误信息
     */
    public function hash_delete($limit=array(0,-1))
    {
        if (empty($this->hash_table_current_table)){
            return FALSE;
        }

        if (empty($this->hash_table_sort_option["by"]))
        {
            $this->hash_table_sort_option["by"] = np_guid_rand();
        }

        if (empty($this->hash_table_sort_option["sort"]))
        {
            $this->hash_table_sort_option["sort"] = "asc";
        }


        $this->hash_where_order_query(false);
        $limit_start = $limit[0] < 0?($this->hash_temp_len+(int)$limit[0]):(int)$limit[0];
        $limit_end = $limit[1] < 0?($this->hash_temp_len+(int)$limit[1]+1):(int)$limit[1];
        if (($limit_end - $limit_start) > self::REDIS_TEMP_PHP_ARRAY_LIMIT && $this->hash_temp_len > self::REDIS_TEMP_PHP_ARRAY_LIMIT)
        {
            $hash_table_delete_store = self::REDIS_HASH_TEMP_TABLE_PIX."DELETE:".MD5(serialize($this->hash_table_sort_option));
            $start = $limit_start;
            while ($start < $limit_end)
            {
                $range = $limit_start + self::REDIS_TEMP_PHP_ARRAY_LIMIT;
                $this->hash_table_sort_option["limit"]=array($limit_start,$range);
                $re =  $this->get_redis()->sort($this->hash_table_temp_table ,$this->hash_table_sort_option);
//                var_dump($re);
                $this->hash_table_delete_by_ids($this->hash_table_current_table,$re);
                $start = $start + self::REDIS_TEMP_PHP_ARRAY_LIMIT + 1;
                unset($re);
            }

//            $this->hash_table_sort_option["limit"]=array($limit_start,$limit_end);
//            $this->hash_table_sort_option["store"] =$hash_table_delete_store;
//            $this->get_redis()->sort($this->hash_table_temp_table ,$this->hash_table_sort_option);
//            $start = 0;
//            while($start < $this->hash_temp_len)
//            {
//                $range = $start+self::REDIS_TEMP_PHP_ARRAY_LIMIT;
//                $re = $this->get_redis()->lRange($hash_table_delete_store,$start,$range);
//                $this->hash_table_delete_by_ids($this->hash_table_current_table,$re);
//                $start = $range+1;
//                unset($re);
//            }

            $this->delete($hash_table_delete_store);
        }else{
            $this->hash_table_sort_option["limit"]=$limit;
            $re = $this->get_redis()->sort($this->hash_table_temp_table ,$this->hash_table_sort_option);
            $this->hash_table_delete_by_ids($this->hash_table_current_table,$re);

        }

//        $keys = $this->get_redis()->sMembers($this->hash_table_temp_table);

        return $this->hash_temp_len;
    }

    /**
     * 根据表ID删除多条数据
     * @param $table_name  表名
     * @param $ids array(id1,id2,id3)
     * @return  TRUE 成功  FALSE 失败
     */
    public function hash_table_delete_by_ids($table_name,$ids)
    {
        $re = false;

        if (!is_array($ids))
        {
            return FALSE;
        }
        $pipeline =  $this->get_redis();
        foreach ($ids as $id){
            $vid =  $this->hash_table_get_table_row_id($table_name,$id);
//            $field_vals=$this->get_redis()->hGet($vid);
            $exists_value = $this->get_redis()->hGetAll($vid);
            foreach ($this->hash_table_indexs as $index)
            {
                $this->get_redis()->multi(Redis::PIPELINE);
                $value = $exists_value[$index];
                if (strlen($value) == 0)
                {
                    continue;
                }
                $index_key = $this->hash_table_get_list_index($table_name, $index, $value);
                $pipeline->sRem($index_key, $id);
                $pipeline->exec();
                if ($this->get_redis()->sCard($index_key) == 0)
                {
                    $pipeline->del($index_key);
                }
            }
            $this->get_redis()->multi(Redis::PIPELINE);
            $id_index= $this->hash_table_get_list_index($table_name,self::REDIS_HASH_TABLE_MAIN_KEY);
            $pipeline->sRem($id_index,$id);

            foreach ($this->hash_table_compares as $compare)
            {
                $compare_key = $this->hash_table_get_list_compare($table_name,$compare);
                $pipeline->zRem($compare_key,$id);
               if ($this->get_redis()->zCard($compare_key) == 0)
               {
                   $pipeline->del($compare_key);
                }
            }

            $re = $pipeline->del($vid);
            $re = $pipeline->exec();
            $re = end($re);
//            var_dump($re);die;
        }
        return $re;
    }

    /**
     * 执行销毁整个表的操作
     * @return FALSE 失败 用hash_table_errors()获取最后一条错误信息
     * TRUE 成功
     */
    public function hash_destroy()
    {
        if (empty($this->hash_table_current_table))
        {
            return FALSE;
        }

        $index_id = self::REDIS_HASH_TABLE_KEY_INDEX_PIX.$this->hash_table_current_table.".#id:";

        $index_struc = self::REDIS_HASH_TABLE_STRUCTURE.$this->hash_table_current_table;

//        $structure = array();
//        $structure["fields"]=$fields;
//        $structure["indexs"]=$indexs;
//        $structure["compares"]=$compares;
//        $structure["exp"]=$exp_time;

        $struc = $this->get($index_struc);
        $struc = unserialize($struc);
        $struc_indexs = $struc["indexs"];
        $struc_compares = $struc["compares"];
        if (is_array($struc_indexs))
        {
            foreach ($struc_indexs as $index_name)
            {
                $index_pix = self::REDIS_HASH_TABLE_KEY_INDEX_PIX.$this->hash_table_current_table.".{$index_name}:*";
                $indexs_key = $this->get_redis()->keys($index_pix);
                if (is_array($indexs_key) && count($indexs_key) > 0)
                {
                    call_user_func_array(array($this->get_redis(),"del"),$indexs_key);
                }
            }
        }

        if (is_array($struc_compares))
        {
            foreach ($struc_compares as $compares)
            {
                $compare_pix = self::REDIS_HASH_TABLE_KEY_COMPARE_PIX.$this->hash_table_current_table.".{$compares}";
                $this->delete($compare_pix);
            }
        }

        $this->delete($index_struc);

        $this->_hash_exec_each_data("_hash_delete_metadata");
//        $this->hash_delete();


        $this->delete($index_id);
        $this->get_redis()->sRem(self::REDIS_HASH_TABLE_STORE,$this->hash_table_current_table);
        return TRUE;
    }

    /**
     * 获取所有错误信息
     * @return array
     * array(
     * array("code"=>错误码,"reason"=>错误原因),
     * array("code"=>错误码,"reason"=>错误原因),
     * )
     */
    public function hash_table_errors(){
        return $this->hash_table_redis_errors;
    }


    public function get_slow_log($limit){
        $count = $this->get_redis()->slowlog("len");
    }

    private function _hash_delete_metadata($re)
    {
            $this->get_redis()->multi(Redis::PIPELINE);
                foreach ($re as $id)
                {
                    $vid =  $this->hash_table_get_table_row_id($this->hash_table_current_table,$id);
                    $this->get_redis()->del($vid);
                }
                $this->get_redis()->exec();
    }

    private function _hash_exec_each_data($func,$params=NULL)
    {

        $count = $this->get_redis()->sCard($this->hash_table_temp_table);
        if ($count > self::REDIS_TEMP_PHP_ARRAY_LIMIT)
        {
            $hash_table_delete_store = self::REDIS_HASH_TEMP_TABLE_PIX."DELETE:".MD5(serialize($this->hash_table_sort_option));
//            $this->hash_table_sort_option["store"] =$hash_table_delete_store;
//            $this->get_redis()->sort($this->hash_table_temp_table ,$this->hash_table_sort_option);
            $this->get_redis()->sUnionStore($hash_table_delete_store,$this->hash_table_temp_table,$hash_table_delete_store);
            $start = 0;
            while($start < $count - self::REDIS_TEMP_PHP_ARRAY_LIMIT)
            {
                $range = $start+self::REDIS_TEMP_PHP_ARRAY_LIMIT;

                $this->get_redis()->multi(Redis::PIPELINE);
                for ($i = $start ;$i <= $range; $i++)
                {
                    $this->get_redis()->sPop($hash_table_delete_store);
                }
                $re = $this->get_redis()->exec();
                call_user_func(array($this,$func),$re,$params);
                $start = $range+1;
                unset($re);
            }
            $re =  $this->get_redis()->sMembers($hash_table_delete_store);
            call_user_func(array($this,$func),$re,$params);
            unset($re);
            $this->delete($hash_table_delete_store);
        }else{
            $re =  $this->get_redis()->sMembers($this->hash_table_temp_table);
            call_user_func(array($this,$func),$re,$params);
        }
    }



//    private function _create_like_temp_table($like_question,$action="and",$cache = true)
//    {
//        $temp_like_table_pix = self::REDIS_HASH_TEMP_TABLE_PIX."LIKE:";
//        $temp_like_table_key = $temp_like_table_pix.MD5(serialize($like_question)."##".$action);
//        $temp_like_table_arr = array();
//        foreach ($like_question as $k=>$v)
//        {
//            if ($k==self::REDIS_HASH_TABLE_MAIN_KEY)
//            {
//                $temp_like_table_arr[] = self::_scan_index_main_key($v,$cache);
//                continue;
//            }
//
//            $temp_like_table_arr[] = self::_scan_index_value($k,$v,$cache);
//        }
//
//        $this->hash_table_like_temp_table  = $temp_like_table_key;
//        if ($this->exists($temp_like_table_key) && $cache==true)
//        {
//            return $temp_like_table_key;
//        }
//
//
//        array_unshift($temp_like_table_arr,$temp_like_table_key);
//        if ($action=="and")
//        {
//            $method = "sInterStore";
//        }else{
//            $method = "sUnionStore";
//        }
//        $this->temp_len=call_user_func_array(array($this->get_redis(),$method),$temp_like_table_arr);
//        $this->get_redis()->expire($temp_like_table_key,self::REDIS_TEMP_TABLE_EXP_TIME);
//        return $temp_like_table_key;
//    }
//
//    private function _scan_index_value($k,$v)
//    {
//        $temp_like_table_pix = self::REDIS_HASH_TEMP_TABLE_PIX."LIKE:";
//        $search_value = self::hash_table_get_list_index($this->hash_table_current_table,$k,$v);
//        $key_temp_table = $temp_like_table_pix . MD5($k."##".$v);
//
//        if ($this->exists($key_temp_table))
//        {
//            return $key_temp_table;
//        }
//
//        $cursor = 0;
//        $loop = TRUE;
//        while ($loop)
//        {
//            $data = $this->get_redis()->scan($cursor,$search_value);
//            $cursor = $data[0];
//            $main_keys = $data[1];
//            if (!is_array($main_keys))
//            {
//                return $key_temp_table;
//            }
//            array_unshift($main_keys,$key_temp_table);
//            call_user_func_array(array($this->get_redis(),"sAdd"),$main_keys);
//            if ($cursor == 0)
//            {
//                $loop = FALSE;
//            }
//        }
//        $this->get_redis()->expire($key_temp_table,self::REDIS_TEMP_TABLE_EXP_TIME);
//        return $key_temp_table;
//    }
//
//    private function _scan_index_main_key($v)
//    {
//        $temp_like_table_pix = self::REDIS_HASH_TEMP_TABLE_PIX."LIKE:";
//        $main_key_table = $this->hash_table_get_list_index($this->hash_table_current_table,self::REDIS_HASH_TABLE_MAIN_KEY);
//        $main_key_temp_table = $temp_like_table_pix . MD5(self::REDIS_HASH_TABLE_MAIN_KEY."##".$v);
//
//        if ($this->exists($main_key_temp_table))
//        {
//            return $main_key_temp_table;
//        }
//
//        $cursor = 0;
//        $loop = TRUE;
//        while ($loop)
//        {
//            $data = $this->get_redis()->sScan($main_key_table,$cursor,$v);
//            $cursor = $data[0];
//            $main_keys = $data[1];
//            if (!is_array($main_keys))
//            {
//                return $main_key_temp_table;
//            }
//            array_unshift($main_keys,$main_key_temp_table);
//            call_user_func_array(array($this->get_redis(),"sAdd"),$main_keys);
//            if ($cursor == 0)
//            {
//                $loop = FALSE;
//            }
//        }
//        $this->get_redis()->expire($main_key_temp_table,self::REDIS_TEMP_TABLE_EXP_TIME);
//        return $main_key_temp_table;
//    }
}
?>